#pragma once

#include "base/assert.hpp"
#include "network/ip_address.hpp"

#include <cstring>
#include <string>

#include <netinet/in.h>
#include <sys/socket.h>

namespace ibox::network {

class SockAddr {
public:
    SockAddr();

    SockAddr(const IpAddress &ip, uint16_t port);
    SockAddr(const struct sockaddr &addr, socklen_t len);
    explicit SockAddr(const struct sockaddr_in &addr);
    // 思考: SockAddr 可以拷贝吗?
    // 当然可以. 而且应该实现.
    // SockAddr有两个成员变量, m_len(基本类型), m_addr(结构体)
    // 思考: SockAddr 应该实现移动语义吗?
    // 没必要
    SockAddr(const SockAddr &other);
    SockAddr &operator=(const SockAddr &other);

    static SockAddr from_string(const std::string &addr_str);

    enum Type {
        None,
        IPv4,
        Local, // Unix Local socket
    };
    Type type() const;
    std::string to_string() const;

    bool get(IpAddress &ip, uint16_t &port) const;

    template <typename T>
    socklen_t to_sockaddr(T &addr) const {
        expect(m_len <= sizeof(T));
        ::memcpy(&addr, &m_addr, m_len);
        return m_len;
    }

    bool operator==(const SockAddr &rhs) const;
    bool operator!=(const SockAddr &rhs) const { return !(*this == rhs); }

private:
    socklen_t m_len = 0;            // 表示地址有效长度
    struct sockaddr_storage m_addr; // 存储地址的结构体
};
} // namespace ibox::network
